hmt <- load_block_group_data(load.cache = T)
15:45:09: Attempting to load cached data.
15:45:09: File at: C:/Users/justin.elszasz/Code/middle_neighborhoods_analysis/data/processed/hmt/hmt_join_acs.rds
cannot open compressed file 'C:/Users/justin.elszasz/Code/middle_neighborhoods_analysis/data/processed/hmt/hmt_join_acs.rds', probable reason 'No such file or directory'15:45:09: Cache does not exist. Creating cache.
15:45:09: Grabbing HMT geospatial table from EGIS server.
15:45:22: Formatting HMT geospatial table.
15:45:23: Loading ACS block group data from Excel sheets.
15:45:23: Joining ACS data to HMT geospatial table.
15:45:23: Directory does not exist. Creating directory.
15:45:23: Cache saved to C:/Users/justin.elszasz/Code/middle_neighborhoods_analysis/data/processed/hmt/hmt_join_acs.rds
15:45:23: HMT data succesfully loaded.
First need to join up the real property data (Open Baltimore) the sales data (provided by Steve, and with deed dates from January 1, 2010 through October 2018) so we have a neighborhood for as many sales as we can.
sales <- sales %>% rename(sales.block = Block, sales.lot = Lot)
real.prop <- real.prop %>% rename(real.block = block, real.lot = lot)
real.prop <- real.prop %>%
mutate(real.block.clean = gsub("^0+", "", real.block),
real.lot.clean = gsub("^0+", "", real.lot))
sales <- sales %>%
mutate(sales.block.clean = gsub("^0+", "", sales.block),
sales.lot.clean = gsub("^0+", "", sales.lot))
sales <- sales %>%
left_join(real.prop,
by = c("sales.block.clean" = "real.block.clean",
"sales.lot.clean" = "real.lot.clean")
)
sales %>% count(is.na(real.block), is.na(real.lot))
1,153 sales didn’t match to a block-lot in the real property table, which means that the block-lot jointly was not in the real prop table.
Also, there are about 16,000 properties in the real prop table that don’t have a neighborhood.
real.prop %>% count(is.na(neighborhood))
So after joining we end up with 9,428 sales that don’t have a neighborhood.
sales %>% count(!is.na(neighborhood))
The real property table also gives if it is principal residence or not, so we’ll also filter for the sales that are for principal residences.
sales %>% count(rescode)
Distribution of city-wide 2018 sales prices:
sales %>%
filter(year(deed.date) == 2018) %>%
ggplot(aes(`Sales Price`)) +
geom_histogram() +
theme_iteam_google_docs() +
xlim(c(0, 500000))

quantile(sales$`Sales Price`, 0.85)
85%
275000
Neighborhood Summary Table, 2015-2017
meet.criteria <- sales %>%
filter(year(deed.date) %in% c(2015, 2016, 2017),
!is.na(neighborhood),
`How Conveyed` == 1,
!grepl("NOT", rescode)) %>%
nrow
We have 16112 samples to work with that are in 2015-2017, have a neighborhood, were an arms-length sale, and are the principal residence.
sales.summary.15_17.by.hood <- sales %>%
filter(year(deed.date) %in% c(2015, 2016, 2017),
!is.na(neighborhood),
`How Conveyed` == 1,
!grepl("NOT", rescode)) %>%
group_by(neighborhood) %>%
summarise(hood.n = n(),
hood.mean = mean(`Sales Price`),
hood.median = median(`Sales Price`),
hood.std = sqrt(sum((`Sales Price`-hood.mean)^2/(hood.n-1))),
hood.95th = quantile(`Sales Price`, probs = .95),
hood.98th = quantile(`Sales Price`, probs = .98),
hood.99th = quantile(`Sales Price`, probs = .99))
sales.summary.15_17.by.hood
Which neighborhoods have less than 20 sales meeting the criteria?
sales.summary.15_17.by.hood %>%
filter(hood.n < 20)
84 neighborhoods have less than 20 sales meeting the criteria. We’ll exclude them going forward so we have a reasonable sample size.
# Join the summaries to the neighborhood boundaries
hoods@data <- hoods@data %>%
left_join(sales.summary.15_17.by.hood,
by = c("label" = "neighborhood"))
98th Percentile
Criteria & Results
sales.hood.98th <- sales %>%
left_join(sales.summary.15_17.by.hood,
by = c("neighborhood" = "neighborhood")) %>%
filter(year(deed.date) == 2018,
hood.n >= 20,
`Sales Price` >= hood.98th,
`How Conveyed` == 1,
!grepl("NOT", rescode)) %>%
arrange(neighborhood)
result.sales <- nrow(sales.hood.98th)
There are 167 sales that meet the following criteria:
- Deed date was between January 1, 2018 and October 5, 2018
- Arms-length sale
- Principal residence
- Neighborhood had at least 20 sales
- 98th percentile for sales prices for their neighborhood.
(If this yield isn’t high enough we can bump it down to the 95th percentile.)
sales.hood.98th$long <- lapply(sales.hood.98th$location.coordinates, function(x) x[1]) %>% unlist()
sales.hood.98th$lat <- lapply(sales.hood.98th$location.coordinates, function(x) x[2]) %>% unlist()
sales.hood.98th.geo <- sales.hood.98th %>% filter(!is.na(long))
sales.hood.98th.geo <- SpatialPointsDataFrame(
sales.hood.98th.geo %>% select(long, lat),
sales.hood.98th.geo,
proj4string = CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"))
sales.hood.98th.geo <-
spTransform(
sales.hood.98th.geo,
CRSobj = CRS("+init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0")
)
Map
library(htmltools)
hoods.labels <- paste0(
hoods$label,
"<br>Median Sales, 2015-2017: ", as.character(hoods$hood.median)
)
sale.labels <- paste0(
sales.hood.98th.geo$`House #`, " ",
sales.hood.98th.geo$`Street Name`, " ",
sales.hood.98th.geo$Suffix,
"<br>Sale Price in 2018: ",
as.character(sales.hood.98th.geo$`Sales Price`),
"<br>New Owner: ", sales.hood.98th.geo$new.owner
)
leaflet() %>%
setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
addProviderTiles(providers$Stamen.TonerLite) %>%
addPolygons(data = hoods,
weight = 2,
color = "black",
opacity = 0.5,
fillOpacity = 0,
label = ~lapply(hoods.labels, HTML)) %>%
addCircleMarkers(data = sales.hood.98th.geo,
radius = 2,
label = ~lapply(sale.labels, HTML))
Full list
sales.hood.98th
Detect jumps
#look for temporal jumps in prices
99th Percentile
Criteria & Results
sales.hood.99th <- sales %>%
left_join(sales.summary.15_17.by.hood,
by = c("neighborhood" = "neighborhood")) %>%
filter(year(deed.date) == 2018,
hood.n >= 20,
`Sales Price` >= hood.99th,
`How Conveyed` == 1,
!grepl("NOT", rescode)) %>%
arrange(neighborhood)
result.sales <- nrow(sales.hood.99th)
There are 167 sales that meet the following criteria:
- Deed date was between January 1, 2018 and October 5, 2018
- Arms-length sale
- Principal residence
- Neighborhood had at least 20 sales
- 99th percentile for sales prices for their neighborhood.
(If this yield isn’t high enough we can bump it down to the 95th percentile.)
sales.hood.99th$long <- lapply(sales.hood.99th$location.coordinates, function(x) x[1]) %>% unlist()
sales.hood.99th$lat <- lapply(sales.hood.99th$location.coordinates, function(x) x[2]) %>% unlist()
sales.hood.99th.geo <- sales.hood.99th %>% filter(!is.na(long))
sales.hood.99th.geo <- SpatialPointsDataFrame(
sales.hood.99th.geo %>% select(long, lat),
sales.hood.99th.geo,
proj4string = CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"))
sales.hood.99th.geo <-
spTransform(
sales.hood.99th.geo,
CRSobj = CRS("+init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0")
)
Map
library(htmltools)
hoods.labels <- paste0(
hoods$label,
"<br>Median Sales, 2015-2017: ", as.character(hoods$hood.median)
)
sale.labels <- paste0(
sales.hood.99th.geo$`House #`, " ",
sales.hood.99th.geo$`Street Name`, " ",
sales.hood.99th.geo$Suffix,
"<br>Sale Price in 2018: ",
as.character(sales.hood.99th.geo$`Sales Price`),
"<br>New Owner: ", sales.hood.99th.geo$new.owner
)
leaflet() %>%
setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
addProviderTiles(providers$Stamen.TonerLite) %>%
addPolygons(data = hoods,
weight = 2,
color = "black",
opacity = 0.5,
fillOpacity = 0,
label = ~lapply(hoods.labels, HTML)) %>%
addCircleMarkers(data = sales.hood.99th.geo,
radius = 2,
label = ~lapply(sale.labels, HTML))
In Middle Neighborhood, 99th Percentile for Neighborhood, Over $250k
The following criteria are used below:
- Deed date was between January 1, 2018 and October 5, 2018
- Arms-length sale
- Principal residence
- Neighborhood had at least 20 sales
- 99th percentile for sales prices for their neighborhood.
- Sale price over $250,000
mid.hoods <- hmt.hood %>% filter(`Predominant Code Ignoring Non-Residential` %in% c("D", "E", "F", "G", "H"))
sales.99th.mid.hood <- subset(sales.hood.99th.geo, tolower(neighborhood) %in% tolower(mid.hoods$Neighborhood))
mid.hoods.geo <- subset(hoods,
tolower(label) %in% tolower(mid.hoods$Neighborhood))
sales.99th.mid.hood.over.250k <- subset(sales.99th.mid.hood,
`Sales Price` > 250000)
mid.hoods.labels <- paste0(
mid.hoods.geo$label,
"<br>Median Sales, 2015-2017: ", as.character(mid.hoods.geo$hood.median)
)
sale.labels <- paste0(
sales.99th.mid.hood.over.250k$`House #`, " ",
sales.99th.mid.hood.over.250k$`Street Name`, " ",
sales.99th.mid.hood.over.250k$Suffix,
"<br>Sale Price in 2018: ",
as.character(sales.99th.mid.hood.over.250k$`Sales Price`),
"<br>New Owner: ", sales.99th.mid.hood.over.250k$new.owner
)
leaflet() %>%
setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
addProviderTiles(providers$Stamen.TonerLite) %>%
addPolygons(data = hoods,
weight = 2,
color = "black",
opacity = 0.5,
fillOpacity = 0,
label = ~lapply(hoods.labels, HTML)) %>%
addPolygons(data = mid.hoods.geo,
weight = 2,
#color = "black",
opacity = 0.0,
fillOpacity = .2,
fillColor = iteam.colors[3],
label = ~lapply(mid.hoods.labels, HTML)) %>%
addCircleMarkers(data = sales.99th.mid.hood.over.250k,
color = iteam.colors[1],
opacity = 1,
radius = 2,
label = ~lapply(sale.labels, HTML))
Full List
sales.99th.mid.hood.over.250k
LS0tDQp0aXRsZTogIlJlY2VudCBTYWxlcyBPdXRsaWVycyINCmF1dGhvcjogIkp1c3RpbiBFbHN6YXN6LCBNYXlvcidzIE9mZmljZSBvZiBJbm5vdmF0aW9uIg0KZW1haWw6ICJqdXN0aW4uZWxzemFzekBiYWx0aW1vcmVjaXR5LmdvdiINCmRhdGU6ICJUaHVyc2RheSwgRmVicnVhcnkgMjgsIDIwMTkiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgZmlnX2hlaWdodDogNQ0KICAgIGZpZ193aWR0aDogMTANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMg0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0UsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZSA9IFRSVUV9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGluY2x1ZGUgPSBULA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA1KQ0KYGBgDQoNCg0KYGBge3J9DQpzb3VyY2UoIi4uL3NyYy8wMF9pbml0aWFsaXplLlIiKQ0Kc2FsZXMgPC0gbG9hZF9zYWxlc19kYXRhKGxvYWQuY2FjaGUgPSBUKQ0KDQpsaWJyYXJ5KFJTb2NyYXRhKQ0KbGlicmFyeShzcCkNCmxpYnJhcnkobGVhZmxldCkNCg0KcmVhbC5wcm9wLnVybCA8LSAiaHR0cHM6Ly9kYXRhLmJhbHRpbW9yZWNpdHkuZ292L3Jlc291cmNlLzZhY3QtcXp1eS5qc29uIg0KcmVhbC5wcm9wIDwtIHJlYWQuc29jcmF0YShyZWFsLnByb3AudXJsLCBhcHBfdG9rZW4gPSBWQVJTJFNPQ1JBVEFfVE9LRU4pDQoNCmhvb2RzIDwtIGdldF9uZWlnaGJvcmhvb2RfYm91bmRhcmllcygpDQpobXQgPC0gbG9hZF9ibG9ja19ncm91cF9kYXRhKGxvYWQuY2FjaGUgPSBUKQ0KaG10Lmhvb2QgPC0gcmVhZF9leGNlbCgiLi4vZGF0YS9yYXcvaG10L0hNVCBieSBOZWlnaGJvcmhvb2QgMjAxNy54bHN4IikNCmBgYA0KDQpGaXJzdCBuZWVkIHRvIGpvaW4gdXAgdGhlIHJlYWwgcHJvcGVydHkgZGF0YSAoW09wZW4gQmFsdGltb3JlXShbaHR0cDovL2RhdGEuYmFsdGltb3JlY2l0eS5nb3YvRmluYW5jaWFsL1JlYWwtUHJvcGVydHktVGF4ZXMvMjd3OS11cnR2dG8pKSB0aGUgc2FsZXMgZGF0YSAocHJvdmlkZWQgYnkgU3RldmUsIGFuZCB3aXRoICoqZGVlZCBkYXRlcyBmcm9tIEphbnVhcnkgMSwgMjAxMCB0aHJvdWdoIE9jdG9iZXIgMjAxOCoqKSBzbyB3ZSBoYXZlIGEgbmVpZ2hib3Job29kIGZvciBhcyBtYW55IHNhbGVzIGFzIHdlIGNhbi4NCg0KYGBge3J9DQpzYWxlcyA8LSBzYWxlcyAlPiUgcmVuYW1lKHNhbGVzLmJsb2NrID0gQmxvY2ssIHNhbGVzLmxvdCA9IExvdCkNCnJlYWwucHJvcCA8LSByZWFsLnByb3AgJT4lIHJlbmFtZShyZWFsLmJsb2NrID0gYmxvY2ssIHJlYWwubG90ID0gbG90KQ0KYGBgDQoNCmBgYHtyfQ0KcmVhbC5wcm9wIDwtIHJlYWwucHJvcCAlPiUNCiAgbXV0YXRlKHJlYWwuYmxvY2suY2xlYW4gPSBnc3ViKCJeMCsiLCAiIiwgcmVhbC5ibG9jayksDQogICAgICAgICByZWFsLmxvdC5jbGVhbiA9IGdzdWIoIl4wKyIsICIiLCByZWFsLmxvdCkpDQoNCnNhbGVzIDwtIHNhbGVzICU+JQ0KICBtdXRhdGUoc2FsZXMuYmxvY2suY2xlYW4gPSBnc3ViKCJeMCsiLCAiIiwgc2FsZXMuYmxvY2spLA0KICAgICAgICAgc2FsZXMubG90LmNsZWFuID0gZ3N1YigiXjArIiwgIiIsIHNhbGVzLmxvdCkpDQpgYGANCg0KYGBge3J9DQpzYWxlcyA8LSBzYWxlcyAlPiUNCiAgbGVmdF9qb2luKHJlYWwucHJvcCwgDQogICAgICAgICAgICBieSA9IGMoInNhbGVzLmJsb2NrLmNsZWFuIiA9ICJyZWFsLmJsb2NrLmNsZWFuIiwNCiAgICAgICAgICAgICAgICAgICAic2FsZXMubG90LmNsZWFuIiA9ICJyZWFsLmxvdC5jbGVhbiIpDQogICAgICAgICAgICApDQpgYGANCg0KYGBge3J9DQpzYWxlcyAlPiUgY291bnQoaXMubmEocmVhbC5ibG9jayksIGlzLm5hKHJlYWwubG90KSkNCmBgYA0KDQoxLDE1MyBzYWxlcyBkaWRuJ3QgbWF0Y2ggdG8gYSBibG9jay1sb3QgaW4gdGhlIHJlYWwgcHJvcGVydHkgdGFibGUsIHdoaWNoIG1lYW5zIHRoYXQgdGhlIGJsb2NrLWxvdCBqb2ludGx5IHdhcyBub3QgaW4gdGhlIHJlYWwgcHJvcCB0YWJsZS4gDQoNCkFsc28sIHRoZXJlIGFyZSBhYm91dCAxNiwwMDAgcHJvcGVydGllcyBpbiB0aGUgcmVhbCBwcm9wIHRhYmxlIHRoYXQgZG9uJ3QgaGF2ZSBhIG5laWdoYm9yaG9vZC4gDQoNCmBgYHtyfQ0KcmVhbC5wcm9wICU+JSBjb3VudChpcy5uYShuZWlnaGJvcmhvb2QpKQ0KYGBgDQoNClNvIGFmdGVyIGpvaW5pbmcgd2UgZW5kIHVwIHdpdGggOSw0Mjggc2FsZXMgdGhhdCBkb24ndCBoYXZlIGEgbmVpZ2hib3Job29kLg0KDQpgYGB7cn0NCnNhbGVzICU+JSBjb3VudCghaXMubmEobmVpZ2hib3Job29kKSkNCmBgYA0KDQpUaGUgcmVhbCBwcm9wZXJ0eSB0YWJsZSBhbHNvIGdpdmVzIGlmIGl0IGlzIHByaW5jaXBhbCByZXNpZGVuY2Ugb3Igbm90LCBzbyB3ZSdsbCBhbHNvIGZpbHRlciBmb3IgdGhlIHNhbGVzIHRoYXQgYXJlIGZvciBwcmluY2lwYWwgcmVzaWRlbmNlcy4NCg0KYGBge3J9DQpzYWxlcyAlPiUgY291bnQocmVzY29kZSkNCmBgYA0KDQpEaXN0cmlidXRpb24gb2YgY2l0eS13aWRlIDIwMTggc2FsZXMgcHJpY2VzOg0KDQpgYGB7cn0NCnNhbGVzICU+JQ0KICBmaWx0ZXIoeWVhcihkZWVkLmRhdGUpID09IDIwMTgpICU+JQ0KICBnZ3Bsb3QoYWVzKGBTYWxlcyBQcmljZWApKSArDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICB0aGVtZV9pdGVhbV9nb29nbGVfZG9jcygpICsNCiAgeGxpbShjKDAsIDUwMDAwMCkpDQpgYGANCg0KYGBge3J9DQpxdWFudGlsZShzYWxlcyRgU2FsZXMgUHJpY2VgLCAwLjg1KQ0KYGBgDQogDQoNCg0KIyBOZWlnaGJvcmhvb2QgU3VtbWFyeSBUYWJsZSwgMjAxNS0yMDE3DQoNCmBgYHtyfQ0KbWVldC5jcml0ZXJpYSA8LSBzYWxlcyAlPiUNCiAgZmlsdGVyKHllYXIoZGVlZC5kYXRlKSAlaW4lIGMoMjAxNSwgMjAxNiwgMjAxNyksDQogICAgICAgICAhaXMubmEobmVpZ2hib3Job29kKSwNCiAgICAgICAgIGBIb3cgQ29udmV5ZWRgID09IDEsDQogICAgICAgICAhZ3JlcGwoIk5PVCIsIHJlc2NvZGUpKSAlPiUNCiAgbnJvdw0KYGBgDQoNCldlIGhhdmUgYHIgbWVldC5jcml0ZXJpYWAgc2FtcGxlcyB0byB3b3JrIHdpdGggdGhhdCBhcmUgaW4gMjAxNS0yMDE3LCBoYXZlIGEgbmVpZ2hib3Job29kLCB3ZXJlIGFuIGFybXMtbGVuZ3RoIHNhbGUsIGFuZCBhcmUgdGhlIHByaW5jaXBhbCByZXNpZGVuY2UuDQoNCmBgYHtyfQ0Kc2FsZXMuc3VtbWFyeS4xNV8xNy5ieS5ob29kIDwtIHNhbGVzICU+JQ0KICBmaWx0ZXIoeWVhcihkZWVkLmRhdGUpICVpbiUgYygyMDE1LCAyMDE2LCAyMDE3KSwNCiAgICAgICAgICFpcy5uYShuZWlnaGJvcmhvb2QpLA0KICAgICAgICAgYEhvdyBDb252ZXllZGAgPT0gMSwNCiAgICAgICAgICFncmVwbCgiTk9UIiwgcmVzY29kZSkpICU+JQ0KICBncm91cF9ieShuZWlnaGJvcmhvb2QpICU+JQ0KICBzdW1tYXJpc2UoaG9vZC5uID0gbigpLA0KICAgICAgICAgICAgaG9vZC5tZWFuID0gbWVhbihgU2FsZXMgUHJpY2VgKSwNCiAgICAgICAgICAgIGhvb2QubWVkaWFuID0gbWVkaWFuKGBTYWxlcyBQcmljZWApLA0KICAgICAgICAgICAgaG9vZC5zdGQgPSBzcXJ0KHN1bSgoYFNhbGVzIFByaWNlYC1ob29kLm1lYW4pXjIvKGhvb2Qubi0xKSkpLA0KICAgICAgICAgICAgaG9vZC45NXRoID0gcXVhbnRpbGUoYFNhbGVzIFByaWNlYCwgcHJvYnMgPSAuOTUpLA0KICAgICAgICAgICAgaG9vZC45OHRoID0gcXVhbnRpbGUoYFNhbGVzIFByaWNlYCwgcHJvYnMgPSAuOTgpLA0KICAgICAgICAgICAgaG9vZC45OXRoID0gcXVhbnRpbGUoYFNhbGVzIFByaWNlYCwgcHJvYnMgPSAuOTkpKQ0KDQpzYWxlcy5zdW1tYXJ5LjE1XzE3LmJ5Lmhvb2QgIA0KYGBgDQoNCldoaWNoIG5laWdoYm9yaG9vZHMgaGF2ZSBsZXNzIHRoYW4gMjAgc2FsZXMgbWVldGluZyB0aGUgY3JpdGVyaWE/DQoNCmBgYHtyfQ0Kc2FsZXMuc3VtbWFyeS4xNV8xNy5ieS5ob29kICU+JQ0KICBmaWx0ZXIoaG9vZC5uIDwgMjApDQpgYGANCg0KODQgbmVpZ2hib3Job29kcyBoYXZlIGxlc3MgdGhhbiAyMCBzYWxlcyBtZWV0aW5nIHRoZSBjcml0ZXJpYS4gV2UnbGwgZXhjbHVkZSB0aGVtIGdvaW5nIGZvcndhcmQgc28gd2UgaGF2ZSBhIHJlYXNvbmFibGUgc2FtcGxlIHNpemUuDQoNCmBgYHtyfQ0KIyBKb2luIHRoZSBzdW1tYXJpZXMgdG8gdGhlIG5laWdoYm9yaG9vZCBib3VuZGFyaWVzDQpob29kc0BkYXRhIDwtIGhvb2RzQGRhdGEgJT4lIA0KICBsZWZ0X2pvaW4oc2FsZXMuc3VtbWFyeS4xNV8xNy5ieS5ob29kLA0KICAgICAgICAgICAgYnkgPSBjKCJsYWJlbCIgPSAibmVpZ2hib3Job29kIikpDQpgYGANCg0KIyA5OHRoIFBlcmNlbnRpbGUNCg0KIyMgQ3JpdGVyaWEgJiBSZXN1bHRzDQoNCmBgYHtyfQ0Kc2FsZXMuaG9vZC45OHRoIDwtIHNhbGVzICU+JQ0KICBsZWZ0X2pvaW4oc2FsZXMuc3VtbWFyeS4xNV8xNy5ieS5ob29kLA0KICAgICAgICAgICAgYnkgPSBjKCJuZWlnaGJvcmhvb2QiID0gIm5laWdoYm9yaG9vZCIpKSAlPiUNCiAgZmlsdGVyKHllYXIoZGVlZC5kYXRlKSA9PSAyMDE4LA0KICAgICAgICAgaG9vZC5uID49IDIwLA0KICAgICAgICAgYFNhbGVzIFByaWNlYCA+PSBob29kLjk4dGgsDQogICAgICAgICBgSG93IENvbnZleWVkYCA9PSAxLA0KICAgICAgICAgIWdyZXBsKCJOT1QiLCByZXNjb2RlKSkgJT4lDQogIGFycmFuZ2UobmVpZ2hib3Job29kKQ0KDQpyZXN1bHQuc2FsZXMgPC0gbnJvdyhzYWxlcy5ob29kLjk4dGgpDQpgYGANCg0KKipUaGVyZSBhcmUgYHIgcmVzdWx0LnNhbGVzYCBzYWxlcyB0aGF0IG1lZXQgdGhlIGZvbGxvd2luZyBjcml0ZXJpYToqKg0KDQotIERlZWQgZGF0ZSB3YXMgYmV0d2VlbiBKYW51YXJ5IDEsIDIwMTggYW5kIE9jdG9iZXIgNSwgMjAxOA0KLSBBcm1zLWxlbmd0aCBzYWxlDQotIFByaW5jaXBhbCByZXNpZGVuY2UNCi0gTmVpZ2hib3Job29kIGhhZCBhdCBsZWFzdCAyMCBzYWxlcw0KLSA5OHRoIHBlcmNlbnRpbGUgZm9yIHNhbGVzIHByaWNlcyBmb3IgdGhlaXIgbmVpZ2hib3Job29kLg0KDQooSWYgdGhpcyB5aWVsZCBpc24ndCBoaWdoIGVub3VnaCB3ZSBjYW4gYnVtcCBpdCBkb3duIHRvIHRoZSA5NXRoIHBlcmNlbnRpbGUuKQ0KDQoNCmBgYHtyfQ0Kc2FsZXMuaG9vZC45OHRoJGxvbmcgPC0gbGFwcGx5KHNhbGVzLmhvb2QuOTh0aCRsb2NhdGlvbi5jb29yZGluYXRlcywgZnVuY3Rpb24oeCkgeFsxXSkgJT4lIHVubGlzdCgpDQoNCnNhbGVzLmhvb2QuOTh0aCRsYXQgPC0gbGFwcGx5KHNhbGVzLmhvb2QuOTh0aCRsb2NhdGlvbi5jb29yZGluYXRlcywgZnVuY3Rpb24oeCkgeFsyXSkgJT4lIHVubGlzdCgpDQpgYGANCg0KDQpgYGB7cn0NCnNhbGVzLmhvb2QuOTh0aC5nZW8gPC0gc2FsZXMuaG9vZC45OHRoICU+JSBmaWx0ZXIoIWlzLm5hKGxvbmcpKQ0KICANCnNhbGVzLmhvb2QuOTh0aC5nZW8gPC0gU3BhdGlhbFBvaW50c0RhdGFGcmFtZSgNCiAgc2FsZXMuaG9vZC45OHRoLmdlbyAlPiUgc2VsZWN0KGxvbmcsIGxhdCksIA0KICBzYWxlcy5ob29kLjk4dGguZ2VvLA0KICBwcm9qNHN0cmluZyA9IENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwIikpDQoNCnNhbGVzLmhvb2QuOTh0aC5nZW8gPC0gDQogIHNwVHJhbnNmb3JtKA0KICAgIHNhbGVzLmhvb2QuOTh0aC5nZW8sIA0KICAgIENSU29iaiA9IENSUygiK2luaXQ9ZXBzZzo0MzI2ICtwcm9qPWxvbmdsYXQgK2RhdHVtPVdHUzg0ICtub19kZWZzICtlbGxwcz1XR1M4NCArdG93Z3M4ND0wLDAsMCIpDQogICAgKQ0KDQpgYGANCg0KIyMgTWFwDQoNCmBgYHtyfQ0KbGlicmFyeShodG1sdG9vbHMpDQoNCmhvb2RzLmxhYmVscyA8LSBwYXN0ZTAoDQogIGhvb2RzJGxhYmVsLA0KICAiPGJyPk1lZGlhbiBTYWxlcywgMjAxNS0yMDE3OiAiLCBhcy5jaGFyYWN0ZXIoaG9vZHMkaG9vZC5tZWRpYW4pDQogIA0KKQ0KDQpzYWxlLmxhYmVscyA8LSBwYXN0ZTAoDQogIHNhbGVzLmhvb2QuOTh0aC5nZW8kYEhvdXNlICNgLCAiICIsDQogIHNhbGVzLmhvb2QuOTh0aC5nZW8kYFN0cmVldCBOYW1lYCwgIiAiLA0KICBzYWxlcy5ob29kLjk4dGguZ2VvJFN1ZmZpeCwgDQogICI8YnI+U2FsZSBQcmljZSBpbiAyMDE4OiAiLCANCiAgYXMuY2hhcmFjdGVyKHNhbGVzLmhvb2QuOTh0aC5nZW8kYFNhbGVzIFByaWNlYCksDQogICI8YnI+TmV3IE93bmVyOiAiLCBzYWxlcy5ob29kLjk4dGguZ2VvJG5ldy5vd25lcg0KKQ0KDQoNCmxlYWZsZXQoKSAlPiUNCiAgc2V0VmlldyhsbmcgPSAtNzYuNiwgbGF0ID0gMzkuMywgem9vbSA9IDExKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkU3RhbWVuLlRvbmVyTGl0ZSkgJT4lIA0KICBhZGRQb2x5Z29ucyhkYXRhID0gaG9vZHMsIA0KICAgICAgICAgICAgICB3ZWlnaHQgPSAyLCANCiAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgICAgICBvcGFjaXR5ID0gMC41LA0KICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAsIA0KICAgICAgICAgICAgICBsYWJlbCA9IH5sYXBwbHkoaG9vZHMubGFiZWxzLCBIVE1MKSkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHNhbGVzLmhvb2QuOTh0aC5nZW8sIA0KICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSB+bGFwcGx5KHNhbGUubGFiZWxzLCBIVE1MKSkNCmBgYA0KDQojIyBGdWxsIGxpc3QNCg0KYGBge3J9DQpzYWxlcy5ob29kLjk4dGggDQpgYGANCg0KIyBEZXRlY3QganVtcHMNCg0KYGBge3J9DQojbG9vayBmb3IgdGVtcG9yYWwganVtcHMgaW4gcHJpY2VzDQpgYGANCg0KIyA5OXRoIFBlcmNlbnRpbGUNCg0KIyMgQ3JpdGVyaWEgJiBSZXN1bHRzDQoNCmBgYHtyfQ0Kc2FsZXMuaG9vZC45OXRoIDwtIHNhbGVzICU+JQ0KICBsZWZ0X2pvaW4oc2FsZXMuc3VtbWFyeS4xNV8xNy5ieS5ob29kLA0KICAgICAgICAgICAgYnkgPSBjKCJuZWlnaGJvcmhvb2QiID0gIm5laWdoYm9yaG9vZCIpKSAlPiUNCiAgZmlsdGVyKHllYXIoZGVlZC5kYXRlKSA9PSAyMDE4LA0KICAgICAgICAgaG9vZC5uID49IDIwLA0KICAgICAgICAgYFNhbGVzIFByaWNlYCA+PSBob29kLjk5dGgsDQogICAgICAgICBgSG93IENvbnZleWVkYCA9PSAxLA0KICAgICAgICAgIWdyZXBsKCJOT1QiLCByZXNjb2RlKSkgJT4lDQogIGFycmFuZ2UobmVpZ2hib3Job29kKQ0KDQpyZXN1bHQuc2FsZXMgPC0gbnJvdyhzYWxlcy5ob29kLjk5dGgpDQpgYGANCg0KKipUaGVyZSBhcmUgYHIgcmVzdWx0LnNhbGVzYCBzYWxlcyB0aGF0IG1lZXQgdGhlIGZvbGxvd2luZyBjcml0ZXJpYToqKg0KDQotIERlZWQgZGF0ZSB3YXMgYmV0d2VlbiBKYW51YXJ5IDEsIDIwMTggYW5kIE9jdG9iZXIgNSwgMjAxOA0KLSBBcm1zLWxlbmd0aCBzYWxlDQotIFByaW5jaXBhbCByZXNpZGVuY2UNCi0gTmVpZ2hib3Job29kIGhhZCBhdCBsZWFzdCAyMCBzYWxlcw0KLSA5OXRoIHBlcmNlbnRpbGUgZm9yIHNhbGVzIHByaWNlcyBmb3IgdGhlaXIgbmVpZ2hib3Job29kLg0KDQooSWYgdGhpcyB5aWVsZCBpc24ndCBoaWdoIGVub3VnaCB3ZSBjYW4gYnVtcCBpdCBkb3duIHRvIHRoZSA5NXRoIHBlcmNlbnRpbGUuKQ0KDQoNCmBgYHtyfQ0Kc2FsZXMuaG9vZC45OXRoJGxvbmcgPC0gbGFwcGx5KHNhbGVzLmhvb2QuOTl0aCRsb2NhdGlvbi5jb29yZGluYXRlcywgZnVuY3Rpb24oeCkgeFsxXSkgJT4lIHVubGlzdCgpDQoNCnNhbGVzLmhvb2QuOTl0aCRsYXQgPC0gbGFwcGx5KHNhbGVzLmhvb2QuOTl0aCRsb2NhdGlvbi5jb29yZGluYXRlcywgZnVuY3Rpb24oeCkgeFsyXSkgJT4lIHVubGlzdCgpDQpgYGANCg0KDQpgYGB7cn0NCnNhbGVzLmhvb2QuOTl0aC5nZW8gPC0gc2FsZXMuaG9vZC45OXRoICU+JSBmaWx0ZXIoIWlzLm5hKGxvbmcpKQ0KICANCnNhbGVzLmhvb2QuOTl0aC5nZW8gPC0gU3BhdGlhbFBvaW50c0RhdGFGcmFtZSgNCiAgc2FsZXMuaG9vZC45OXRoLmdlbyAlPiUgc2VsZWN0KGxvbmcsIGxhdCksIA0KICBzYWxlcy5ob29kLjk5dGguZ2VvLA0KICBwcm9qNHN0cmluZyA9IENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwIikpDQoNCnNhbGVzLmhvb2QuOTl0aC5nZW8gPC0gDQogIHNwVHJhbnNmb3JtKA0KICAgIHNhbGVzLmhvb2QuOTl0aC5nZW8sIA0KICAgIENSU29iaiA9IENSUygiK2luaXQ9ZXBzZzo0MzI2ICtwcm9qPWxvbmdsYXQgK2RhdHVtPVdHUzg0ICtub19kZWZzICtlbGxwcz1XR1M4NCArdG93Z3M4ND0wLDAsMCIpDQogICAgKQ0KDQpgYGANCg0KIyMgTWFwDQoNCmBgYHtyfQ0KbGlicmFyeShodG1sdG9vbHMpDQoNCmhvb2RzLmxhYmVscyA8LSBwYXN0ZTAoDQogIGhvb2RzJGxhYmVsLA0KICAiPGJyPk1lZGlhbiBTYWxlcywgMjAxNS0yMDE3OiAiLCBhcy5jaGFyYWN0ZXIoaG9vZHMkaG9vZC5tZWRpYW4pDQogIA0KKQ0KDQpzYWxlLmxhYmVscyA8LSBwYXN0ZTAoDQogIHNhbGVzLmhvb2QuOTl0aC5nZW8kYEhvdXNlICNgLCAiICIsDQogIHNhbGVzLmhvb2QuOTl0aC5nZW8kYFN0cmVldCBOYW1lYCwgIiAiLA0KICBzYWxlcy5ob29kLjk5dGguZ2VvJFN1ZmZpeCwgDQogICI8YnI+U2FsZSBQcmljZSBpbiAyMDE4OiAiLCANCiAgYXMuY2hhcmFjdGVyKHNhbGVzLmhvb2QuOTl0aC5nZW8kYFNhbGVzIFByaWNlYCksDQogICI8YnI+TmV3IE93bmVyOiAiLCBzYWxlcy5ob29kLjk5dGguZ2VvJG5ldy5vd25lcg0KKQ0KDQoNCmxlYWZsZXQoKSAlPiUNCiAgc2V0VmlldyhsbmcgPSAtNzYuNiwgbGF0ID0gMzkuMywgem9vbSA9IDExKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkU3RhbWVuLlRvbmVyTGl0ZSkgJT4lIA0KICBhZGRQb2x5Z29ucyhkYXRhID0gaG9vZHMsIA0KICAgICAgICAgICAgICB3ZWlnaHQgPSAyLCANCiAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgICAgICBvcGFjaXR5ID0gMC41LA0KICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAsIA0KICAgICAgICAgICAgICBsYWJlbCA9IH5sYXBwbHkoaG9vZHMubGFiZWxzLCBIVE1MKSkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHNhbGVzLmhvb2QuOTl0aC5nZW8sIA0KICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSB+bGFwcGx5KHNhbGUubGFiZWxzLCBIVE1MKSkNCmBgYA0KDQpgYGB7cn0NCmVtaWx5Lmxpc3QgPC0gYygiNDAxMSBCQVJSSU5HVE9OIiwNCiAgICAgICAgICAgICAgICAiRE9SQ0hFU1RFUiIsDQogICAgICAgICAgICAgICAgIjIzMTkgTU9OVElDRUxMTyIpDQoNCnNhbGVzLmhvb2QuOTh0aC5nZW9AZGF0YSAlPiUgDQogIGZpbHRlcihncmVwbChwYXN0ZShlbWlseS5saXN0LCBjb2xsYXBzZT0ifCIpLCBwcm9wZXJ0eWFkZHJlc3MpKQ0KYGBgDQoNCiMgSW4gTWlkZGxlIE5laWdoYm9yaG9vZCwgOTl0aCBQZXJjZW50aWxlIGZvciBOZWlnaGJvcmhvb2QsIE92ZXIgJDI1MGsNCg0KVGhlIGZvbGxvd2luZyBjcml0ZXJpYSBhcmUgdXNlZCBiZWxvdzoNCg0KLSBEZWVkIGRhdGUgd2FzIGJldHdlZW4gSmFudWFyeSAxLCAyMDE4IGFuZCBPY3RvYmVyIDUsIDIwMTgNCi0gQXJtcy1sZW5ndGggc2FsZQ0KLSBQcmluY2lwYWwgcmVzaWRlbmNlDQotIE5laWdoYm9yaG9vZCBoYWQgYXQgbGVhc3QgMjAgc2FsZXMNCi0gOTl0aCBwZXJjZW50aWxlIGZvciBzYWxlcyBwcmljZXMgZm9yIHRoZWlyIG5laWdoYm9yaG9vZC4NCi0gU2FsZSBwcmljZSBvdmVyICQyNTAsMDAwDQoNCg0KYGBge3J9DQptaWQuaG9vZHMgPC0gaG10Lmhvb2QgJT4lIGZpbHRlcihgUHJlZG9taW5hbnQgQ29kZSBJZ25vcmluZyBOb24tUmVzaWRlbnRpYWxgICVpbiUgYygiRCIsICJFIiwgIkYiLCAiRyIsICJIIikpDQoNCnNhbGVzLjk5dGgubWlkLmhvb2QgPC0gc3Vic2V0KHNhbGVzLmhvb2QuOTl0aC5nZW8sIHRvbG93ZXIobmVpZ2hib3Job29kKSAlaW4lIHRvbG93ZXIobWlkLmhvb2RzJE5laWdoYm9yaG9vZCkpDQpgYGANCg0KDQpgYGB7cn0NCg0KbWlkLmhvb2RzLmdlbyA8LSBzdWJzZXQoaG9vZHMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgdG9sb3dlcihsYWJlbCkgJWluJSB0b2xvd2VyKG1pZC5ob29kcyROZWlnaGJvcmhvb2QpKQ0KDQpzYWxlcy45OXRoLm1pZC5ob29kLm92ZXIuMjUwayA8LSBzdWJzZXQoc2FsZXMuOTl0aC5taWQuaG9vZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgU2FsZXMgUHJpY2VgID4gMjUwMDAwKQ0KDQptaWQuaG9vZHMubGFiZWxzIDwtIHBhc3RlMCgNCiAgbWlkLmhvb2RzLmdlbyRsYWJlbCwNCiAgIjxicj5NZWRpYW4gU2FsZXMsIDIwMTUtMjAxNzogIiwgYXMuY2hhcmFjdGVyKG1pZC5ob29kcy5nZW8kaG9vZC5tZWRpYW4pDQogIA0KKQ0KDQpzYWxlLmxhYmVscyA8LSBwYXN0ZTAoDQogIHNhbGVzLjk5dGgubWlkLmhvb2Qub3Zlci4yNTBrJGBIb3VzZSAjYCwgIiAiLA0KICBzYWxlcy45OXRoLm1pZC5ob29kLm92ZXIuMjUwayRgU3RyZWV0IE5hbWVgLCAiICIsDQogIHNhbGVzLjk5dGgubWlkLmhvb2Qub3Zlci4yNTBrJFN1ZmZpeCwgDQogICI8YnI+U2FsZSBQcmljZSBpbiAyMDE4OiAiLCANCiAgYXMuY2hhcmFjdGVyKHNhbGVzLjk5dGgubWlkLmhvb2Qub3Zlci4yNTBrJGBTYWxlcyBQcmljZWApLA0KICAiPGJyPk5ldyBPd25lcjogIiwgc2FsZXMuOTl0aC5taWQuaG9vZC5vdmVyLjI1MGskbmV3Lm93bmVyDQopDQoNCg0KbGVhZmxldCgpICU+JQ0KICBzZXRWaWV3KGxuZyA9IC03Ni42LCBsYXQgPSAzOS4zLCB6b29tID0gMTEpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRTdGFtZW4uVG9uZXJMaXRlKSAlPiUgDQogIGFkZFBvbHlnb25zKGRhdGEgPSBob29kcywgDQogICAgICAgICAgICAgIHdlaWdodCA9IDIsIA0KICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMCwgDQogICAgICAgICAgICAgIGxhYmVsID0gfmxhcHBseShob29kcy5sYWJlbHMsIEhUTUwpKSAlPiUNCiAgYWRkUG9seWdvbnMoZGF0YSA9IG1pZC5ob29kcy5nZW8sIA0KICAgICAgICAgICAgICB3ZWlnaHQgPSAyLCANCiAgICAgICAgICAgICAgI2NvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuMCwNCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAuMiwNCiAgICAgICAgICAgICAgZmlsbENvbG9yID0gaXRlYW0uY29sb3JzWzNdLA0KICAgICAgICAgICAgICBsYWJlbCA9IH5sYXBwbHkobWlkLmhvb2RzLmxhYmVscywgSFRNTCkpICU+JQ0KICBhZGRDaXJjbGVNYXJrZXJzKGRhdGEgPSBzYWxlcy45OXRoLm1pZC5ob29kLm92ZXIuMjUwaywgDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSBpdGVhbS5jb2xvcnNbMV0sDQogICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsDQogICAgICAgICAgICAgICAgICAgcmFkaXVzID0gMiwNCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IH5sYXBwbHkoc2FsZS5sYWJlbHMsIEhUTUwpKQ0KYGBgDQoNCiMjIEZ1bGwgTGlzdA0KDQpgYGB7cn0NCnNhbGVzLjk5dGgubWlkLmhvb2Qub3Zlci4yNTBrDQpgYGANCg0K